跳到主要内容

DataFrame 索引机制

在 pandas 中,索引(Index)本质上是一组用于数据选取和对齐的标签。它不需要像数据库主键那样严格唯一,但索引的唯一性和排序状态会直接影响许多操作的行为和性能。

核心索引器

pandas 提供了两套主要的索引方式:基于标签(Label)和基于位置(Position)。

import pandas as pd

df = pd.DataFrame({
"order_id": ["order-42", "order-43"],
"status": ["paid", "pending"],
"customer_id": ["C1", "C2"],
"amount": [120, 80],
}, index=["order-42", "order-43"])
df.loc["order-42", ["status", "amount"]] # 基于标签选取
df.iloc[0:10, [1, 3]] # 基于位置选取
df.at["order-42", "status"] # 基于标签获取单个标量
df.iat[0, 1] # 基于位置获取单个标量

切片语义差异:

  • .loc (标签):当标签存在时,切片通常是闭区间(包含起止两端)。
  • .iloc (位置):遵循 Python 标准的半开区间语义(左闭右开,即 start:stop 不包含 stop)。

设置与重置索引

indexed = df.set_index("order_id", verify_integrity=True)
plain = indexed.reset_index()
  • 唯一性校验:如果业务逻辑要求索引唯一,务必设置 verify_integrity=True,否则重复值会导致后续操作报错或行为异常。
  • 排序:如果后续操作依赖有序切片(如范围查询)或时间序列特性,请先对索引进行排序。

MultiIndex(多级索引)

MultiIndex 允许在单个轴上拥有多个标签层级。当数据具有天然层级结构,且需要频繁进行分层选择、重塑(Reshape)或分组输出时,它非常高效。

sales = pd.DataFrame({
"country": ["Canada", "Canada"], "city": ["Toronto", "Ottawa"], "amount": [10, 20]
})
by_region = sales.set_index(["country", "city"]).sort_index()
toronto = by_region.loc[("Canada", "Toronto")] # 精确到城市
canada = by_region.loc["Canada"] # 选取整个国家

使用建议:

  • 如果层级结构只是临时的,或者下游工具(如某些数据库或 API)只接受扁平表格,优先使用普通列。
  • 使用 reset_index 可以将多级索引的层级还原为普通列。

对齐陷阱(Alignment Hazard)

pandas 的一个核心特性是自动对齐:涉及 pandas 对象的赋值和算术运算,默认会按标签进行对齐,而不是按位置。

  • 风险:如果你本意是按位置操作,但数据标签不一致,pandas 会静默地插入 NaN 或丢弃数据,导致难以排查的 Bug。
  • 最佳实践:如果意图是按位置操作,请显式使用 .values.to_numpy() 转换,并严格验证数组长度。

静默对齐既是 pandas 强大灵活性的来源,也是产生意外缺失值(Missing Values)的最常见原因。

选择结果的形状与重复标签

上例标签唯一,因此 df.loc["order-42", ["status", "amount"]] 返回长度为 2 的 Series,df.iloc[0:10, [1, 3]] 返回 (2, 2) 的 DataFrame,两个标量示例都返回 "paid"。要保留 (1, 2) 的表格形状,用 df.loc[["order-42"], ["status", "amount"]]。位置切片遇到末尾会截断;单个位置越界则抛出 IndexError

Index 是有序标签序列,不是数学上的集合:它允许重复。因此,看似选择单个标签的操作也可能返回多行。依赖标量结果前,需检查 df.index.is_uniquedf.columns.is_uniquereindex 会按要求构造标签,并为不存在的标签补缺失值;严格的 loc 标签查找则会抛出 KeyError

参考资料

探索关联打开关联网络